﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TrackSystem.Entities.Extensions;
using TrackSystem.IO.Utils;

namespace TrackSystem.IO.Formula
{
    /// <summary>
    /// 操作数定义
    /// </summary>
    public class OperandDef : object
    {
        #region 属性定义
        public DataTypeEnum DataType { get; set; }
        public bool bVal { get; set; }
        public int iVal { get; set; }
        public double dVal { get; set; }
        public string? sVal { get; set; }
        #endregion
        
        /// <summary>
        /// 将字符串类型转换为相应类型的数据
        /// </summary>
        /// <param name="value">字符串</param>
        /// <param name="op">定义</param>
        /// <returns></returns>
        public static bool TryParse(string value, out OperandDef op)
        {
            // 尝试将字符串转换为整数 
            if (int.TryParse(value, out int iVal))
            {
                op = new OperandDef(iVal);
                return true;
            }

            // 尝试将字符串转换为双精度浮点数
            if (double.TryParse(value, out double dVal))
            {
                op = new OperandDef(dVal);
                return true;
            }

            // 检查是否为布尔值
            if (bool.TryParse(value, out bool bVal))
            {
                op =  new OperandDef(bVal);
                return true;
            }

            // 如果都不是，则认为是字符串
            if (value[0] == '"' && value[^1] == '"' || value[0] == '\'' && value[^1] == '\'')
            {
                value = value[1..^1]; // 去掉引号
                op =  new OperandDef(value);
                return true;
            }

            op= new OperandDef();
            return false;
        }

        #region 构造函数
        public OperandDef()
        {
            DataType = DataTypeEnum.INTEGER;
            dVal = 0;
            iVal = 0;
            bVal = false;
            sVal = "";
        }

        public OperandDef(bool b)
        {
            DataType = DataTypeEnum.BOOLEAN;
            bVal = b;
        }

        public OperandDef(int n)
        {
            DataType = DataTypeEnum.INTEGER;
            iVal = n;
        }

        public OperandDef(double d)
        {
            DataType = DataTypeEnum.DOUBLE;
            dVal = d;
        }

        public OperandDef(string s)
        {
            DataType = DataTypeEnum.STRING;
            sVal = s;
        }

        public OperandDef(byte[] bw)
        {
            DataType = DataTypeEnum.STRING;
            StringBuilder s = new StringBuilder();
            foreach (byte b in bw)
            {
                s.Append($"{b:X02} ");
            }
            sVal = s.ToString();
        }

        public OperandDef(OperandDef op)
        {
            DataType = op.DataType;
            bVal = op.bVal;
            iVal = op.iVal;
            dVal = op.dVal;
            sVal = op.sVal;
        }

        public OperandDef(DataTypeEnum dataType)
        {
            DataType = dataType;
            switch (dataType)
            {
                case DataTypeEnum.BOOLEAN: bVal = false; break;
                case DataTypeEnum.INTEGER: iVal = 0; break;
                case DataTypeEnum.DOUBLE: dVal = 0; break;
                case DataTypeEnum.STRING: sVal = ""; break;
            }
        }

        public OperandDef Clone()
        {
            OperandDef op = new OperandDef(DataType);
            op.bVal = bVal;
            op.iVal = iVal;
            op.dVal = dVal;
            op.sVal = sVal;
            return op;
        }

        /// <summary>
        /// 字符串方式值值
        /// </summary>
        /// <param name="s"></param>
        public void SetStringValue(string s)
        {
            if (TryParse(s, out OperandDef op))
            {
                if (op.DataType == DataType)
                {
                    Assign(op);
                }
                else if (op.DataType == DataTypeEnum.INTEGER && DataType == DataTypeEnum.DOUBLE)
                {
                    dVal = op.iVal;
                }
                if (op.DataType == DataTypeEnum.DOUBLE && DataType == DataTypeEnum.INTEGER)
                {
                    iVal = (int)op.dVal;
                }
            }
              
        }
        #endregion
        
        #region  一元运算符
        public static OperandDef operator -(OperandDef op)
        {
            if (op.DataType == DataTypeEnum.BOOLEAN || op.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException("参数错误，取负数操作不能是布尔型或者字符型");
            }
            return new OperandDef()
            {
                DataType = op.DataType,
                iVal = -op.iVal,
                dVal = -op.dVal
            };
        }

        public static OperandDef operator +(OperandDef op)
        {
            if (op.DataType == DataTypeEnum.BOOLEAN || op.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException("参数错误，取正数操作不能是布尔型或者字符型");
            }
            return new OperandDef(op);
        }

        public static OperandDef operator ++(OperandDef op)
        {
            if (op.DataType == DataTypeEnum.BOOLEAN || op.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException("参数错误，取正数操作不能是布尔型或者字符型");
            }
            return new OperandDef()
            {
                DataType = op.DataType,
                iVal = op.iVal + 1,
                dVal = op.dVal + 1
            };
        }
        
        public static OperandDef operator --(OperandDef op)
        {
            if (op.DataType == DataTypeEnum.BOOLEAN || op.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException("参数错误，取正数操作不能是布尔型或者字符型");
            }
            return new OperandDef()
            {
                DataType = op.DataType,
                iVal = op.iVal - 1,
                dVal = op.dVal - 1
            };
        }
        
        public static OperandDef operator !(OperandDef op)
        {
            if (op.DataType != DataTypeEnum.BOOLEAN)
            {
                throw new ArgumentException("参数错误，取反操作不能是布尔型");
            }

            return new OperandDef(!op.bVal);
        }
        #endregion

        #region 数学运算符
        public static OperandDef operator +(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType == DataTypeEnum.STRING && op2.DataType == DataTypeEnum.STRING)
                return new OperandDef(op1.sVal + op2.sVal);
            
            if (op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING ||
                op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException("参数错误，加法运算不能是布尔型或者字符型");
            }

            if (op1.DataType == DataTypeEnum.DOUBLE && op2.DataType == DataTypeEnum.INTEGER)
            {
                return new OperandDef(op1.dVal + op2.iVal);
            }

            if (op1.DataType == DataTypeEnum.INTEGER && op2.DataType == DataTypeEnum.DOUBLE)
            {
                return new OperandDef(op1.iVal + op2.dVal);
            }

            return new OperandDef()
            {
                DataType = op1.DataType,
                iVal = op1.iVal + op2.iVal,
                dVal = op1.dVal + op2.dVal
            };
        }

        public static OperandDef operator -(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING ||
                op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException("参数错误，减法运算作不能是布尔型或者字符型");
            }

            return op1.DataType switch
            {
                DataTypeEnum.DOUBLE when op2.DataType == DataTypeEnum.INTEGER => new OperandDef(op1.dVal - op2.iVal),
                DataTypeEnum.INTEGER when op2.DataType == DataTypeEnum.DOUBLE => new OperandDef(op1.iVal - op2.dVal),
                _ => new OperandDef()
                {
                    DataType = op1.DataType, iVal = op1.iVal - op2.iVal, dVal = op1.dVal - op2.dVal
                }
            };
        }

        public static OperandDef operator *(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING ||
                op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException("参数错误，乘法运算不能是布尔型或者字符型");
            }

            return op1.DataType switch
            {
                DataTypeEnum.DOUBLE when op2.DataType == DataTypeEnum.INTEGER => new OperandDef(op1.dVal * op2.iVal),
                DataTypeEnum.INTEGER when op2.DataType == DataTypeEnum.DOUBLE => new OperandDef(op1.iVal * op2.dVal),
                _ => new OperandDef()
                {
                    DataType = op1.DataType, iVal = op1.iVal * op2.iVal, dVal = op1.dVal * op2.dVal
                }
            };
        }

        public static OperandDef operator /(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING ||
                op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException("参数错误，除法法运算作不能是布尔型或者字符型");
            }

            if (op1.DataType == DataTypeEnum.INTEGER)
            {
                op1.DataType = DataTypeEnum.DOUBLE;
                op1.dVal = op1.iVal;
            }

            if (op2.DataType == DataTypeEnum.INTEGER)
            {
                op2.DataType = DataTypeEnum.DOUBLE;
                op2.dVal = op2.iVal;
            }

            if (Math.Abs(op2.dVal) <= double.Epsilon)
                throw new ArgumentException("DIV 被除数为零错误");

            return new OperandDef(op1.dVal / op2.dVal);
        }

        public static OperandDef operator ^(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING ||
                op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException("参数错误，乘方操作不能是布尔型或者字符型");
            }

            if (op1.DataType == DataTypeEnum.INTEGER)
            {
                op1.DataType = DataTypeEnum.DOUBLE;
                op1.dVal = op1.iVal;
            }

            if (op2.DataType == DataTypeEnum.INTEGER)
            {
                op2.DataType = DataTypeEnum.DOUBLE;
                op2.dVal = op2.iVal;
            }

            return new OperandDef(Math.Pow(op1.dVal, op2.dVal));
        }

        public static OperandDef operator %(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType != DataTypeEnum.INTEGER || op1.DataType != DataTypeEnum.INTEGER)        {
                throw new ArgumentException("参数错误，%操作必须是整型");
            }

            return new OperandDef(op1.iVal % op2.iVal);
        }

        #endregion

        #region 比较运算符
        public static OperandDef operator >=(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING ||
                op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException($"{op1} >= {op2} 操作>=没有实现");
            }

            if (op1.DataType == DataTypeEnum.INTEGER && op2.DataType == DataTypeEnum.DOUBLE)
                return new OperandDef(op1.iVal >= op2.dVal);

            if (op1.DataType == DataTypeEnum.DOUBLE && op2.DataType == DataTypeEnum.INTEGER)
                return new OperandDef(op1.dVal >= op2.iVal);

            return op1.DataType == DataTypeEnum.INTEGER ? 
                new OperandDef(op1.iVal >= op2.iVal) :
                new OperandDef(op1.dVal >= op2.dVal);
        }

        public static OperandDef operator <=(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING ||
                op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException($"{op1} <= {op2} 操作<=没有实现");
            }

            if (op1.DataType == DataTypeEnum.INTEGER && op2.DataType == DataTypeEnum.DOUBLE)
                return new OperandDef(op1.iVal <= op2.dVal);

            if (op1.DataType == DataTypeEnum.DOUBLE && op2.DataType == DataTypeEnum.INTEGER)
                return new OperandDef(op1.dVal <= op2.iVal);

            return op1.DataType == DataTypeEnum.INTEGER ?
                new OperandDef(op1.iVal <= op2.iVal) :
                new OperandDef(op1.dVal <= op2.dVal);
        }

        public static OperandDef operator >(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING ||
                op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException($"{op1} > {op2} 操作>没有实现");
            }

            if (op1.DataType == DataTypeEnum.INTEGER && op2.DataType == DataTypeEnum.DOUBLE)
                return new OperandDef(op1.iVal > op2.dVal);

            if (op1.DataType == DataTypeEnum.DOUBLE && op2.DataType == DataTypeEnum.INTEGER)
                return new OperandDef(op1.dVal > op2.iVal);

            return op1.DataType == DataTypeEnum.INTEGER ?
                new OperandDef(op1.iVal > op2.iVal) :
                new OperandDef(op1.dVal > op2.dVal);
        }

        public static OperandDef operator <(OperandDef op1, OperandDef op2)
        {
            if (op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING ||
                op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
            {
                throw new ArgumentException($"{op1} < {op2} 操作<没有实现");
            }

            if (op1.DataType == DataTypeEnum.INTEGER && op2.DataType == DataTypeEnum.DOUBLE)
                return new OperandDef(op1.iVal < op2.dVal);

            if (op1.DataType == DataTypeEnum.DOUBLE && op2.DataType == DataTypeEnum.INTEGER)
                return new OperandDef(op1.dVal < op2.iVal);

            return op1.DataType == DataTypeEnum.INTEGER ?
                new OperandDef(op1.iVal < op2.iVal) :
                new OperandDef(op1.dVal < op2.dVal);
        }

        public static OperandDef operator ==(OperandDef op1, OperandDef op2)
        {
            if ((op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING 
                || op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
                && op1.DataType != op2.DataType)
            {
                throw new NotImplementedException($"{op1.DataType} == {op2.DataType} 没有实现");
            }
            return new OperandDef(op1.GetHashCode() == op2.GetHashCode());
        }

        public static OperandDef operator !=(OperandDef op1, OperandDef op2)
        {
            if ((  op1.DataType == DataTypeEnum.BOOLEAN || op1.DataType == DataTypeEnum.STRING
                || op2.DataType == DataTypeEnum.BOOLEAN || op2.DataType == DataTypeEnum.STRING)
                && op1.DataType != op2.DataType)
            {
                throw new NotImplementedException($"{op1.DataType} != {op2.DataType} 没有实现");
            }
            return new OperandDef(op1.GetHashCode() != op2.GetHashCode());
        }
        #endregion

        #region 重载函数
        /// <summary>
        /// 比较函数
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object? obj)
        {
            if (obj == null) return false;
            return obj.GetHashCode() == GetHashCode();
        }

        /// <summary>
        /// 获取哈希值
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            if (DataType == DataTypeEnum.DOUBLE)
            {
                int d = (int)dVal;
                if (Math.Abs(d - dVal) < double.Epsilon) 
                    return d.GetHashCode();
            }

            int hashId = DataType switch
            {
                DataTypeEnum.BOOLEAN => bVal.GetHashCode(),
                DataTypeEnum.INTEGER => iVal.GetHashCode(),
                DataTypeEnum.DOUBLE => dVal.GetHashCode(),
                DataTypeEnum.STRING => sVal == null ? 0 : sVal.GetHashCode(),
                _ => 0
            };

            return hashId;
        }

        /// <summary>
        /// 转化为字符串
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            switch (DataType)
            {
                case DataTypeEnum.BOOLEAN:  return $"{bVal}";
                case DataTypeEnum.INTEGER:  return $"{iVal}";
                case DataTypeEnum.DOUBLE:   return $"{dVal}";
                case DataTypeEnum.STRING: return sVal ??= "";
            }

            return "";
        }

        /// <summary>
        /// 赋值函数
        /// </summary>
        /// <param name="op">输入的数据</param>
        /// <exception cref="NotImplementedException"></exception>
        public void Assign(OperandDef op)
        {
            if (DataType == op.DataType)
            {
                 DataType = op.DataType;
                 iVal = op.iVal;
                 dVal = op.dVal;
                 sVal = op.sVal;
                 return;
            }
           
            if (op.DataType == DataTypeEnum.INTEGER && DataType == DataTypeEnum.DOUBLE)
            {
                dVal = op.iVal;
                return;
            }

            throw new NotImplementedException($"=: 不能将类型{op.DataType}数据赋值给类型{DataType}");
        }
        #endregion
        
    }
}
